int limit,
int flags) {
- GlyphLayout gl = GlyphLayout.get(null); // !!! no custom layout engines
+ GlyphLayout gl = GlyphLayout.get();
StandardGlyphVector gv = gl.layout(this, frc, text,
start, limit-start, flags, null);
GlyphLayout.done(gl);
/*
- * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
int start = source.getStart();
int length = source.getLength();
- GlyphLayout gl = GlyphLayout.get(null); // !!! no custom layout engines
+ GlyphLayout gl = GlyphLayout.get();
gv = gl.layout(font, frc, context, start, length, flags, null); // ??? use textsource
GlyphLayout.done(gl);
/*
- * Copyright (c) 2003, 2020, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
// cached glyph layout data for reuse
private static volatile GlyphLayout cache; // reusable
- private LayoutEngineFactory _lef; // set when get is called, unset when done is called
private TextRecord _textRecord; // the text we're working on, used by iterators
private ScriptRun _scriptRuns; // iterator over script runs
private FontRunIterator _fontRuns; // iterator over physical fonts in a composite
private int _typo_flags;
private int _offset;
- public static final class LayoutEngineKey {
- private Font2D font;
- private int script;
- private int lang;
-
- LayoutEngineKey() {
- }
-
- LayoutEngineKey(Font2D font, int script, int lang) {
- init(font, script, lang);
- }
-
- void init(Font2D font, int script, int lang) {
- this.font = font;
- this.script = script;
- this.lang = lang;
- }
-
- LayoutEngineKey copy() {
- return new LayoutEngineKey(font, script, lang);
- }
-
- Font2D font() {
- return font;
- }
-
- int script() {
- return script;
- }
-
- int lang() {
- return lang;
- }
-
- public boolean equals(Object rhs) {
- if (this == rhs) return true;
- if (rhs == null) return false;
- try {
- LayoutEngineKey that = (LayoutEngineKey)rhs;
- return this.script == that.script &&
- this.lang == that.lang &&
- this.font.equals(that.font);
- }
- catch (ClassCastException e) {
- return false;
- }
- }
-
- public int hashCode() {
- return script ^ lang ^ font.hashCode();
- }
- }
-
- public static interface LayoutEngineFactory {
- /**
- * Given a font, script, and language, determine a layout engine to use.
- */
- public LayoutEngine getEngine(Font2D font, int script, int lang);
-
- /**
- * Given a key, determine a layout engine to use.
- */
- public LayoutEngine getEngine(LayoutEngineKey key);
- }
-
- public static interface LayoutEngine {
- /**
- * Given a strike descriptor, text, rtl flag, and starting point, append information about
- * glyphs, positions, and character indices to the glyphvector data, and advance the point.
- *
- * If the GVData does not have room for the glyphs, throws an IndexOutOfBoundsException and
- * leave pt and the gvdata unchanged.
- */
- public void layout(FontStrikeDesc sd, float[] mat, float ptSize, int gmask,
- int baseIndex, TextRecord text, int typo_flags, Point2D.Float pt, GVData data);
- }
-
/**
- * Return a new instance of GlyphLayout, using the provided layout engine factory.
- * If null, the system layout engine factory will be used.
+ * Return a possibly recycled instance of GlyphLayout
*/
- public static GlyphLayout get(LayoutEngineFactory lef) {
- if (lef == null) {
- lef = SunLayoutEngine.instance();
- }
+ public static GlyphLayout get() {
GlyphLayout result = null;
synchronized(GlyphLayout.class) {
if (cache != null) {
if (result == null) {
result = new GlyphLayout();
}
- result._lef = lef;
return result;
}
* of GlyphLayout objects.
*/
public static void done(GlyphLayout gl) {
- gl._lef = null;
cache = gl; // object reference assignment is thread safe, it says here...
}
}
}
- int lang = -1; // default for now
-
Font2D font2D = FontUtilities.getFont2D(font);
if (font2D instanceof FontSubstitution) {
font2D = ((FontSubstitution)font2D).getCompositeFont2D();
}
int gmask = _fontRuns.getGlyphMask();
int pos = _fontRuns.getPos();
- nextEngineRecord(start, pos, script, lang, pfont, gmask);
+ nextEngineRecord(start, pos, script, pfont, gmask);
start = pos;
}
}
while (_scriptRuns.next()) {
int limit = _scriptRuns.getScriptLimit();
int script = _scriptRuns.getScriptCode();
- nextEngineRecord(start, limit, script, lang, font2D, 0);
+ nextEngineRecord(start, limit, script, font2D, 0);
start = limit;
}
}
this._gvdata.init(capacity);
}
- private void nextEngineRecord(int start, int limit, int script, int lang, Font2D font, int gmask) {
+ private void nextEngineRecord(int start, int limit, int script, Font2D font, int gmask) {
EngineRecord er = null;
if (_ercount == _erecords.size()) {
er = new EngineRecord();
} else {
er = _erecords.get(_ercount);
}
- er.init(start, limit, font, script, lang, gmask);
+ er.init(start, limit, font, script, gmask);
++_ercount;
}
private int limit;
private int gmask;
private int eflags;
- private LayoutEngineKey key;
- private LayoutEngine engine;
+ private Font2D font;
+ private int script;
EngineRecord() {
- key = new LayoutEngineKey();
}
- void init(int start, int limit, Font2D font, int script, int lang, int gmask) {
+ void init(int start, int limit, Font2D font, int script, int gmask) {
this.start = start;
this.limit = limit;
+ this.font = font;
+ this.script = script;
this.gmask = gmask;
- this.key.init(font, script, lang);
this.eflags = 0;
// only request canonical substitution if we have combining marks
break;
}
}
-
- this.engine = _lef.getEngine(key); // flags?
}
void layout() {
_textRecord.start = start;
_textRecord.limit = limit;
- engine.layout(_sd, _mat, ptSize, gmask, start - _offset, _textRecord,
+ SunLayoutEngine.layout(font, script, _sd, _mat, ptSize, gmask, start - _offset, _textRecord,
_typo_flags | eflags, _pt, _gvdata);
}
}
/*
+ * Copyright (c) 2003, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
import java.awt.geom.Point2D;
import java.lang.foreign.MemorySegment;
-import java.lang.ref.SoftReference;
import java.util.concurrent.ConcurrentHashMap;
import java.util.WeakHashMap;
-/*
- * different ways to do this
- * 1) each physical font2d keeps a hashtable mapping scripts to layout
- * engines, we query and fill this cache.
- * 2) we keep a mapping independent of font using the key Most likely
- * few fonts will be used, so option 2 seems better
- *
- * Once we know which engine to use for a font, we always know, so we
- * shouldn't have to recheck each time we do layout. So the cache is
- * ok.
- *
- * Should we reuse engines? We could instantiate an engine for each
- * font/script pair. The engine would hold onto the table(s) from the
- * font that it needs. If we have multiple threads using the same
- * engine, we still need to keep the state separate, so the native
- * engines would still need to be allocated for each call, since they
- * keep their state in themselves. If they used the passed-in GVData
- * arrays directly (with some checks for space) then since each GVData
- * is different per thread, we could reuse the layout engines. This
- * still requires a separate layout engine per font, because of the
- * table state in the engine. If we pushed that out too and passed it
- * in with the native call as well, we'd be ok if the layout engines
- * keep all their process state on the stack, but I don't know if this
- * is true. Then we'd basically just be down to an engine index which
- * we pass into native and then invoke the engine code (now a
- * procedure call, not an object invocation) based on a switch on the
- * index. There would be only half a dozen engine objects then, not
- * potentially half a dozen per font. But we'd have to stack-allocate
- * some state that included the pointer to the required font tables.
- *
- * Seems for now that the way to do things is to come in with a
- * selector and the font. The selector indicates which engine to use,
- * the engine is stack allocated and initialized with the required
- * font tables (the selector indicates which). Then layout is called,
- * the contents are copied (or not), and the stack is destroyed on
- * exit. So the association is between the font/script (layout engine
- * desc) and one of a few permanent engine objects, which are
- * handed the key when they need to process something. In the native
- * case, the engine holds an index, and just passes it together with
- * the key info down to native. Some default cases are the 'default
- * layout' case that just runs the c2gmapper, this stays in java and
- * just uses the mapper from the font/strike. Another default case
- * might be the unicode arabic shaper, since this doesn't care about
- * the font (or script or lang?) it wouldn't need to extract this
- * data. It could be (yikes) ported back to java even to avoid
- * upcalls to check if the font supports a particular unicode
- * character.
- *
- * I'd expect that the majority of scripts use the default mapper for
- * a particular font. Loading the hashtable with 40 or so keys 30+ of
- * which all map to the same object is unfortunate. It might be worth
- * instead having a per-font list of 'scripts with non-default
- * engines', e.g. the factory has a hashtable mapping fonts to 'script
- * lists' (the factory has this since the design potentially has other
- * factories, though I admit there's no client for this yet and no
- * public api) and then the script list is queried for the script in
- * question. it can be preloaded at creation time with all the
- * scripts that don't have default engines-- either a list or a hash
- * table, so a null return from the table means 'default' and not 'i
- * don't know yet'.
- *
- * On the other hand, in most all cases the number of unique
- * script/font combinations will be small, so a flat hashtable should
- * suffice.
- * */
-public final class SunLayoutEngine implements LayoutEngine, LayoutEngineFactory {
+public final class SunLayoutEngine {
+
static {
FontManagerNativeLibrary.load();
}
- private LayoutEngineKey key;
-
- private static LayoutEngineFactory instance;
-
- public static LayoutEngineFactory instance() {
- if (instance == null) {
- instance = new SunLayoutEngine();
- }
- return instance;
- }
-
private SunLayoutEngine() {
- // actually a factory, key is null so layout cannot be called on it
- }
-
- public LayoutEngine getEngine(Font2D font, int script, int lang) {
- return getEngine(new LayoutEngineKey(font, script, lang));
- }
-
- // !!! don't need this unless we have more than one sun layout engine...
- public LayoutEngine getEngine(LayoutEngineKey key) {
- ConcurrentHashMap<LayoutEngineKey, LayoutEngine> cache = cacheref.get();
- if (cache == null) {
- cache = new ConcurrentHashMap<>();
- cacheref = new SoftReference<>(cache);
- }
-
- LayoutEngine e = cache.get(key);
- if (e == null) {
- LayoutEngineKey copy = key.copy();
- e = new SunLayoutEngine(copy);
- cache.put(copy, e);
- }
- return e;
- }
- private SoftReference<ConcurrentHashMap<LayoutEngineKey, LayoutEngine>> cacheref =
- new SoftReference<>(null);
-
- private SunLayoutEngine(LayoutEngineKey key) {
- this.key = key;
}
private static final WeakHashMap<Font2D, FaceRef> facePtr =
new WeakHashMap<>();
- private long getFacePtr(Font2D font2D) {
+ private static long getFacePtr(Font2D font2D) {
FaceRef ref;
synchronized (facePtr) {
ref = facePtr.computeIfAbsent(font2D, FaceRef::new);
}
- public void layout(FontStrikeDesc desc, float[] mat, float ptSize, int gmask,
+ public static void layout(Font2D font, int script, FontStrikeDesc desc, float[] mat, float ptSize, int gmask,
int baseIndex, TextRecord tr, int typo_flags,
Point2D.Float pt, GVData data) {
- Font2D font = key.font();
FontStrike strike = font.getStrike(desc);
if (useFFM) {
MemorySegment face = HBShaper.getFace(font);
if (face != null) {
HBShaper.shape(font, strike, ptSize, mat, face,
- tr.text, data, key.script(),
+ tr.text, data, script,
tr.start, tr.limit, baseIndex, pt,
typo_flags, gmask);
}
long pFace = getFacePtr(font);
if (pFace != 0) {
shape(font, strike, ptSize, mat, pFace,
- tr.text, data, key.script(),
+ tr.text, data, script,
tr.start, tr.limit, baseIndex, pt,
typo_flags, gmask);
}